package com.panopset.flywheel;

import static com.panopset.Util.*;

import java.io.IOException;
import java.io.StringWriter;
import java.util.StringTokenizer;
import java.util.Vector;

import com.panopset.StringLineSupplier;
import com.panopset.map.NamedMap;

/**
 * <h5>l - List</h5>
 *
 * <p>Example 1</p>
<pre>
${&#064;l someListFile.txt}
</pre>
 *
 * <p>Example 2</p>
<pre>
${&#064;p listFileName}someListFile.txt${&#064;q}
${&#064;l &#064;listFileName}
 * </pre>
 * <p>
 * Read the given file, and for each line execute the template from this list
 * command up until its matching q command.
 * </p><p>
 * You may also specify a jar resource, for example
 * <pre>
 *   /com/company/myList.txt
 * </pre>
 * </p><p>
 * If no set of tokens is defined,
 * each line will be stored in variable <b>1</b>.
 * If there are tokens defined, the line will be split by your tokens, and stored in
 * variables named after integers, in order.
 * Tokens may be defined by the Flywheel.Builder.tokens method, or by specifying
 * them in the reserved variable name <b>${com.panopset.flywheel.tokens}</b>
 * </p><p>
 * Another way to split the lines is by columns.  If no tokens are defined, you
 * may specify comma delimited column separators in the reserved
 * variable name <b>${com.panopset.flywheel.splits}</b>.  For example <b>5,10</b>.
 * </p><p>
 *
 * List commands can be nested, as each list command has its own cascading variable
 * definitions.  Just remember that any top level list has to define numbered variables
 * as different variable names in order for lower level lists to use them.
 * </p>
 * @author Karl Dinwiddie
 */
public class CommandList extends MatchableCommand implements UserMatchableCommand, ReservedWords {

    public static String getShortHTMLText() {
        return "${&#064;l someListFile.txt}";
    }

    public CommandList(String source, String innerPiece, Template template) {
        super(source, innerPiece, template);
    }

    @Override
    public void resolve(StringWriter sw) {
        SourceFile sourceFile = new SourceFile(template.flywheel, getParams());
        Vector<String> lines;
        try {
            lines = sourceFile.isValid() ? sourceFile.getSourceLines() :
                loadLinesFromResource(getParams());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String tokens = template.flywheel.get(TOKENS);
        String splits = template.flywheel.get(SPLITS);
        template.flywheel.mapStack.push(new NamedMap<String, String>(generateUniqueKey() + UNDERSCORE + getParams()));
        for (String s : lines) {
            if (isPopulated(tokens)) {
                StringTokenizer st = new StringTokenizer(s, tokens);
                int i = 0;
                while (st.hasMoreTokens()) {
                    i++;
                    template.flywheel.put("" + i, processValue(st.nextToken()));
                }
            } else if (isPopulated(splits)){
                StringTokenizer st = new StringTokenizer(splits, COMMA);
                int count = 1;
                int startAt = 0;
                int split = 0;
                while (st.hasMoreElements()) {
                    split = Integer.parseInt(st.nextToken())-1;
                    if (s.length() >= split) {
                        template.flywheel.put("" + count++, processValue(s.substring(startAt, split)));
                        startAt = split;
                    }
                }
                if (s.length() > split) {
                    template.flywheel.put("" + count++, processValue(s.substring(split)));
                }
            } else {
                template.flywheel.put("1", processValue(s));
            }
            super.resolve(sw);
        }
        template.flywheel.mapStack.pop();
    }

    private String processValue(String s) {
        StringWriter sw = new StringWriter();
        new Template(template.flywheel, new StringLineSupplier(new String[]{s})).exec(sw);
        return sw.toString();
    }
}
